// PsychoFight.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Elements.h"
#include "DrawWindow.h"
#include "Game.h"
#include "ResManager.h"
#include "Statemachine.h"

using namespace Gdiplus;
#define WS_STYLE	(WS_OVERLAPPED |WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX)
HINSTANCE DrawWindow::hInstance = 0;

/////////////////////////////////////////////////////////////////////
//buffer operation

void DrawWindow::ReCreateBackBuffer(){
	DeleteBufferDC();
	HDC hdc = ::GetDC(hWnd);
	BackBufferDC = ::CreateCompatibleDC ( hdc );
	EmptyDC = ::CreateCompatibleDC ( hdc );
	ResetBackBuffer();
}

HDC DrawWindow::CreateBufferDC(int width_i, int height_i){
	HBITMAP hbm = G.RM->CreateBufferBmp(width_i, height_i);//CreateBuffer(width_i, height_i);
	HDC hdc = CreateEmptyDC();
	SelectObject(hdc, hbm);
	return hdc;
}

HDC DrawWindow::CreateEmptyDC(){
	_ASSERT(hWnd);
	HDC hdc = ::GetDC(hWnd);
	HDC h = ::CreateCompatibleDC ( hdc );
	ReleaseDC(hWnd, hdc);
	return h;
}

void DrawWindow::ResetBackBuffer(){
	//I think this small code piece do
	//not actually need Resource management. DO-IT-HERE is clearer.
	HDC dc = GetDC(hWnd);
	//always THE_BB in the BackBuffer DC...
	//because nobody select other HBITMAPs into BackBufferDC,
	//only draw on it...
	HBITMAP THE_BB = ::CreateCompatibleBitmap(dc, WidthClient, HeightClient);
	ReleaseDC(hWnd, dc);
	HBITMAP mono = (HBITMAP)::SelectObject(BackBufferDC, THE_BB);
	DeleteObject(mono);
}

void DrawWindow::Put2EmptyDC(HBITMAP hBm_i){
	SelectObject(EmptyDC, hBm_i);
}

void DrawWindow::DeleteEmptyDC(HDC& hdc_io){
	::DeleteDC(hdc_io); 
	hdc_io = 0;
}

void DrawWindow::DeleteBufferDC(HDC& hdc_io){
	if(hdc_io){
		HDC dc = GetDC(hWnd);
		HBITMAP Mono= CreateCompatibleBitmap(dc, 0, 0);
		ReleaseDC(hWnd, dc);
		HBITMAP old = (HBITMAP)SelectObject(hdc_io, Mono);//push out the old one.
		DeleteEmptyDC(hdc_io);//delete DC first to make sure nothing was duplicated
		DeleteObject(Mono);//then delete the mono
		G.RM->UnloadBmp(old); //then the thing was pushed out.
	}
}

void DrawWindow::DeleteBufferDC(){
	DeleteBufferDC(BackBufferDC);//delete THE_BB, then delete BBDC..
	// don't delete the thing that currently in the EmptyDC..
	//so using Delete*Empty*DC
	DeleteEmptyDC(EmptyDC);
}

/////////////////////////////////////////////////////////////////////
//
DrawWindow::DrawWindow(ResourceManager* fm, CTimer* pT){
	_ASSERT(pT);//for wndproc using;
	Inactive = false;
	BBWidth = 0;
	BBHeight = 0;
	BackBufferDC = 0;
	EmptyDC = 0;
	LastFrameTicks = 0;
	Ini.SetFileManager(fm);

	Thunk.Init(WndProcHub, this);
	LoadInitiateFromProfileFile();

	Create();
}

void DrawWindow::Finish(){
	SaveInitiateToProfileFile();
	DeleteBufferDC();
}

void DrawWindow::WaitTilOkToDrawFrame( )
{
	//frames should not be stoped event the timer is stopped...only the 
	//objects in game could be stop.
	//while(Inactive){Sleep(500); return;}
	TimeElapsed= G.T->GetElapsedTimeBySystem(LastFrameTicks); 
	while(TimeElapsed < TPF){
		//sleep to save CPU time. otherwise the cpu will be 100% in use.
		Sleep(10);
		TimeElapsed += G.T->GetElapsedTimeBySystem(LastFrameTicks);
	}
}

void DrawWindow::Present( )
{	
	iFPS = (int)(1.0/TimeElapsed);
	HDC hdc = GetDC(hWnd);
	StretchBlt(hdc , 0, 0, WidthClient, HeightClient, BackBufferDC ,0,0, WidthClient, HeightClient, SRCCOPY);
	ReleaseDC(hWnd, hdc);
	FrameCount++;
	//ResetBackBuffer();
}


wchar_t FPSstr[100];

void DrawWindow::Draw( ){

	////TODO: this will be a display bar
	//swprintf(FPSstr, L"FPS: %d" , iFPS);
	////SetBkMode(BackBufferDC, TRANSPARENT);
	//SetTextColor(BackBufferDC, 0x00000000);
	//TextOutW(BackBufferDC, 10,10, FPSstr, (int)wcslen(FPSstr));


	//Font myFont(L"Arial", 26);
	//PointF origin(10.0f, 10.0f);
	//SolidBrush blackBrush(Color(255,255,255));
	//pBBG->DrawString(String, 100, &myFont, origin, &blackBrush); 
}

void DrawWindow::LoadInitiateFromProfileFile(){
	const unsigned int WindowMax = 2000;
	const unsigned int WindowMin = 200;
	const unsigned int FixFPSMax = 101;
	const unsigned int FixFPSMin = 30;

	XClient = 100;
	YClient = 100;
	WidthClient = 768;
	HeightClient = 512;
	iFPS = 40;
	
	BBWidth = WidthClient; //Fixed value for backbuffer size. 
	BBHeight = HeightClient;
	
	FullScreen = false;


	Ini.IntoFile(0);
	Ini.IntoApp(L"Window");
	Ini.AssertGetValue(L"XClient", XClient, 0, WindowMax);
	Ini.AssertGetValue(L"YClient", YClient, 0, WindowMax);
	//Ini.AssertGetValue(L"WidthClient", WidthClient, 0, WindowMax);
	//Ini.AssertGetValue(L"HeightClient", HeightClient, 0, WindowMax);
	Ini.IntoApp(L"Render");
	Ini.AssertGetValue(L"FPS", iFPS, FixFPSMin, FixFPSMax);

	RECT HitRect = {XClient, YClient, WidthClient+XClient, HeightClient+YClient};
	AdjustWindowRect(&HitRect, WS_STYLE, TRUE);
	NcX = HitRect.left; NcY = HitRect.top;
	NcWidth = HitRect.right - HitRect.left;
	NcHeight = HitRect.bottom - HitRect.top;

	TPF = (double)iFPS;
	TPF = 1.0 / TPF;
}

void DrawWindow::SaveInitiateToProfileFile(){

	Ini.IntoFile(0);
	Ini.IntoApp(L"Window");
	Ini.SetValue(L"XClient",XClient);
	Ini.SetValue(L"YClient",YClient);
	Ini.SetValue(L"WidthClient",WidthClient);
	Ini.SetValue(L"HeightClient",HeightClient);

}

void DrawWindow::OnSize( ){
	ReCreateBackBuffer(); 
	OnMove();
}

void DrawWindow::OnMove( ){
	RECT HitRect;
	GetClientRect(hWnd, &HitRect);
	if(!HitRect.right || !HitRect.bottom) return;

	POINT point = { HitRect.left, HitRect.top};
	ClientToScreen(hWnd, &point);

	XClient = point.x;
	YClient = point.y;
	WidthClient = HitRect.right;
	HeightClient =HitRect.bottom;

	GetWindowRect(hWnd, &HitRect);
	NcX = HitRect.left;
	NcY = HitRect.top;
	NcWidth = HitRect.right - HitRect.left;
	NcHeight = HitRect.bottom - HitRect.top;

	//char arg[100]={0};
	//sprintf(arg, "%d: XClient:%d, YClient:%d, W:%d, H:%d\n", GID, XClient, YClient, WidthClient, HeightClient);
	//OutputDebugStringA(arg);
}

void DrawWindow::OnLostFocus(bool I){

}

void DrawWindow::OnMinimized(bool I){
	Inactive = I;
	if(I) G.SM->PauseGame();
	else G.SM->ResumeGame();
	OnLostFocus(I);
}

void DrawWindow::ChangeScreenMode(bool FullScreen){
}


void DrawWindow::OnkeyDown(BYTE key){
	G.INP->PushKey(key, TRUE);
}

void DrawWindow::OnkeyUp(BYTE key){
	G.INP->PushKey(key, FALSE);
}

bool DrawWindow::IsProcessingMessage(){

	MSG msg = {0};
	BOOL ret = FALSE;

	if(Inactive){
		GetMessageW(&msg, 0,  0 ,0);
		ret = true;
	}else{
		ret = PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE );
	}

	if(ret){
		if(msg.message ==WM_QUIT) 
			G.SM->FinishGame();
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

	return (ret != FALSE);
}

LRESULT CALLBACK DrawWindow::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_SYSCOMMAND:
		if(wParam == SC_MAXIMIZE){
			ChangeScreenMode(true);
			return 0;
		}
		break;

	case WM_SYSKEYDOWN:
		if(wParam == VK_MENU) return 0; 
		//no menu selection by ALT key. but combination is
		//OK, like ALT + F4. because ALT+F4 is not ALT, but 
		//F4 by SYSKEY message. without ALT, F4 will be apeared 
		//in KEY message.
		break;
	case WM_KEYDOWN:
		OnkeyDown((BYTE)wParam);
		return 0;

	case WM_KEYUP:
		OnkeyUp((BYTE)wParam);
		return 0;

	case WM_NCLBUTTONDBLCLK:
		if(wParam == HTCAPTION) {
			ChangeScreenMode(true);
			return 0;
		}
		break;

	case WM_ACTIVATEAPP:
		if(wParam == TRUE){
			OnLostFocus(false);
		}
		if(wParam == FALSE){
			OnLostFocus(true);
		}
		break;

	case WM_SIZE:
		if(wParam == SIZE_MINIMIZED){
			OnMinimized(true);
			break;
		}

		if(wParam == SIZE_RESTORED)
			OnMinimized(false);
		
		OnSize( );
		break;

	case WM_MOVE:
		OnMove(); 
		break;

	case WM_PAINT:
		break;

	case WM_ERASEBKGND:return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
} // WndProc


void DrawWindow::Create(){
	WNDCLASS            wndClass;
	wndClass.style          = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc    = StartWndProc;
	wndClass.cbClsExtra     = 0;
	wndClass.cbWndExtra     = 0;
	wndClass.hInstance      = hInstance ;
	wndClass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName   =MAKEINTRESOURCE(IDC_PSYCHOFIGHT);
	wndClass.lpszClassName  = L"PsychoFight";
	RegisterClass(&wndClass);
	hWnd = CreateWindow(L"PsychoFight", L"PsychoFight", WS_STYLE, 
		NcX, NcY, NcWidth, NcHeight, NULL, NULL, hInstance,this);
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
}

////////////////////////////////////////////////////////////////////////////
//some function will never need to be changed (hopefully)

LRESULT CALLBACK DrawWindow::WndProcHub(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	return ((DrawWindow*)hWnd)->WndProc(message, wParam, lParam);
}

LRESULT CALLBACK DrawWindow::StartWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	if(message == WM_NCCREATE){
		DrawWindow *w = 0;
		w = (DrawWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
		if(w){
			w->hWnd = hWnd;
#pragma warning( push )
#pragma warning(disable:4311)
			SetWindowLongW(hWnd,GWL_WNDPROC,(LONG)&w->Thunk.thunk);
#pragma warning(pop)
		}
	}
	return DefWindowProc(hWnd,message,wParam,lParam);
}
